#include <QXmlStreamWriter>
#include <QDebug>
#include <QStandardItemModel>
#include <QFileInfo>
#include <QTextCodec>

#include <QDir>
#include "pacient.h"
#include "manager.h"
#include "defs.h"


Pacient::Pacient() :
    mFName(QString()),
    mMName(QString()),
    mLName(QString()),
    mSex(UkrString("чол")),
    mComment(QString()),
    mFotoPath(QString()),
    mDob(QDate()),
    mHFather(-1.0),
    mHMother(-1.0),
    mOperationsModel (NULL),
    mInspectionsModel (NULL),
    mRentgensModel (NULL)
{
    createAndSetupModels();
    mDob = QDate::currentDate();
    mSmoke = false;
    mAlko = false;
    mNarco = false;

    id = NotInDataBase;
}

Pacient::Pacient(const QString &fName, const QString &mName, const QString &lName, const QDate &dob, const QString &sex) :
    mFName(fName),
    mMName(mName),
    mLName(lName),
    mSex(sex),
    mDob (dob),
    mHFather(-1.0),
    mHMother(-1.0),
    mOperationsModel (NULL),
    mInspectionsModel (NULL),
    mRentgensModel (NULL)
{
    createAndSetupModels();
    mSmoke = false;
    mAlko = false;
    mNarco = false;

    id = NotInDataBase;
}


Pacient::~Pacient()
{
    delete mInspectionsModel;
    delete mOperationsModel;
    delete mRentgensModel;
}

double Pacient::ageAtDate(const QDate &date)
{
    double age = mDob.daysTo(date) / (30.0 * 12.0);
    return age;
}

void Pacient::setFromVariantList(const QVariantList &vl)
{
    for (int i=0; i<PacientFieldCount; ++i)
    {
        QVariant cur = vl.at(i);
        switch (i) {
        case PF_Id:
            id = cur.toInt(); break;
        case PF_FName:
            mFName = cur.toString(); break;
        case PF_MName:
            mMName = cur.toString(); break;
        case PF_LName:
            mLName = cur.toString(); break;
        case PF_Sex:
            mSex = cur.toString(); break;
        case PF_Dob:
            mDob =  QDate::fromString( cur.toString(), SqlDateFormat); break;
        case PF_HFather:
            mHFather = cur.toDouble(); break;
        case PF_HMother:
            mHMother = cur.toDouble(); break;
        case PF_BloodType:
            mBlood.first = cur.toInt(); break;
        case PF_BloodRh:
            mBlood.second = cur.toInt(); break;
        case PF_Region:
            mRegion = cur.toInt(); break;
        case PF_Town:
            mTown = cur.toString(); break;
        case PF_Comment:
            mComment = cur.toString(); break;
        case PF_Smoke:
            mSmoke = cur.toInt()==1 ? true : false; break;
        case PF_Alko:
            mAlko = cur.toInt()==1 ? true : false; break;
        case PF_Narco:
            mNarco = cur.toInt()==1 ? true : false; break;
        default: break;
        }
    }
    App->mainWindow()->showPacientOnForm();
}


void Pacient::addOperation(const QVariantList &newOperation)
{
    const int newRow = mOperationsModel->rowCount();
    for (int i=0; i<OperationColCount; i++)
    {
        QStandardItem *item = new QStandardItem(
                    QString(newOperation.at(i).toString()));
        mOperationsModel->setItem(newRow, i, item);
    }
}

int Pacient::operationsCount() const
{
    return mOperationsModel->rowCount();
}

QStandardItemModel* Pacient::operations()
{
    return mOperationsModel;
}

void Pacient::addInspection(const QVariantList &newInspection)
{
//    qDebug () << newInspection;

    const int newRow = mInspectionsModel->rowCount();
    for (int i=0; i<InspectionColCount; ++i)
    {
        QStandardItem *item = new QStandardItem(newInspection.at(i).toString());
        mInspectionsModel->setItem(newRow, i, item);
    }
}

int Pacient::inspectionsCount() const
{
    return mInspectionsModel->rowCount();
}

QStandardItemModel* Pacient::inspections()
{
    return mInspectionsModel;
}

void Pacient::addRentgen(QVariantList &newRentgen)
{
    const int newRow = mRentgensModel->rowCount();

    for (int i=0; i<RENT_COL_COUNT; i++)
    {
        QStandardItem *si = new QStandardItem(newRentgen.at(i).toString());
        mRentgensModel->setItem(newRow, i, si);
    }
}

int Pacient::rentgensCount() const
{
    return mRentgensModel->rowCount();
}

QStandardItemModel *Pacient::rentgens()
{
    return mRentgensModel;
}


bool Pacient::saveToXml(const QString &path)
{
    //    Правила запису в файл:
    //    * якщо елементу немає\не визначений -- записати порожній

    QFile file(path);
    if (!file.open(QIODevice::WriteOnly))
        return false;

    file.resize(0);                 // занулення файлу
    QXmlStreamWriter xml(&file);

    xml.setAutoFormatting(true);

    xml.writeStartDocument();

    xml.writeStartElement("pacient");
    xml.writeAttribute("ver", QString::number(PACIENT_VERSION)); // версія формату

    // запис паспортних даних пацієнта
    xml.writeTextElement("fname", mFName);
    xml.writeTextElement("mname", mMName);
    xml.writeTextElement("lname", mLName);
    xml.writeTextElement("dob", mDob.toString(DateFormat));
    xml.writeTextElement("sex", mSex);
    xml.writeEmptyElement("blood");
    xml.writeAttribute("type", QString::number( mBlood.first ));
    xml.writeAttribute("rh", QString::number( mBlood.second ));

    xml.writeEmptyElement("lives");
    xml.writeAttribute("region", QString::number( mRegion ));
    xml.writeAttribute("town", mTown );

    xml.writeEmptyElement("habits");
    xml.writeAttribute("smoke", mSmoke ? "1" : "0");
    xml.writeAttribute("alko", mAlko ? "1" : "0");
    xml.writeAttribute("narco", mNarco ? "1" : "0");


    QFileInfo fotoFile(mFotoPath);
    xml.writeTextElement("foto", fotoFile.fileName());

    // запис секції parents
    xml.writeStartElement("parents");                           // BEGIN parents
    xml.writeEmptyElement("father");
    xml.writeAttribute("height",
                       mHFather == -1.0 ? "" : QString("%1").arg(mHFather));
    xml.writeEmptyElement("mother");
    xml.writeAttribute("height",
                       mHMother == -1.0 ? "" : QString("%1").arg(mHMother));
    xml.writeEndElement();                                      // END parents

    // запис секції operations
    xml.writeStartElement("operations");
    for (int i=0; i < mOperationsModel->rowCount(); ++i)
    {
        xml.writeStartElement("operation");
        xml.writeAttribute("date", mOperationsModel->item(i, OC_Date)->text());
        xml.writeAttribute("age", mOperationsModel->item(i, OC_Age)->text());
        xml.writeAttribute("limb", mOperationsModel->item(i, OC_Limb)->text());
        xml.writeAttribute("bone", mOperationsModel->item(i, OC_Bone)->text());
        xml.writeAttribute("location", mOperationsModel->item(i, OC_Location)->text());
        xml.writeAttribute("method", mOperationsModel->item(i, OC_Method)->text());
        xml.writeCharacters( mOperationsModel->item(i, OC_Comment)->text() );
        xml.writeEndElement();
    }
    xml.writeEndElement();  // END operations

    // запис секції inspections
    xml.writeStartElement("inspections");               // BEGIN inspectoinS

    for (int i=0; i<mInspectionsModel->rowCount(); i++)
    {
        xml.writeStartElement("inspection");            // BEGIN inspection

        xml.writeTextElement("date",
                             mInspectionsModel->item(i, IC_Date)->text());
        xml.writeTextElement("age",
                             mInspectionsModel->item(i, IC_Age)->text());

        xml.writeTextElement("limb",
                             mInspectionsModel->item(i, IC_Limb)->text());
        xml.writeEmptyElement("stegno");
        xml.writeAttribute("u", mInspectionsModel->item(i, IC_UStegno)->text());
        xml.writeAttribute("z", mInspectionsModel->item(i, IC_ZStegno)->text());

        xml.writeEmptyElement("vgk");
        xml.writeAttribute("u", mInspectionsModel->item(i, IC_UVgk)->text());
        xml.writeAttribute("z", mInspectionsModel->item(i, IC_ZVgk)->text());

        xml.writeEmptyElement("mgk");
        xml.writeAttribute("u", mInspectionsModel->item(i, IC_UMgk)->text());
        xml.writeAttribute("z", mInspectionsModel->item(i, IC_ZMgk)->text());

        xml.writeTextElement("stopa", mInspectionsModel->item(i, IC_Stopa)->text());

        xml.writeTextElement("promeni", mInspectionsModel->item(i, IC_Promeni)->text());

        xml.writeEmptyElement("param");
        xml.writeAttribute("height",
                           mInspectionsModel->item(i, IC_Height)->text());
        xml.writeAttribute("weight",
                           mInspectionsModel->item(i, IC_Weight)->text());
        xml.writeEmptyElement("prognoz");
        xml.writeAttribute("make",
                           mInspectionsModel->item(i, IC_Prognoz)->text());

        xml.writeEndElement();                          // END inspection
    }
    xml.writeEndElement();                              // END inspectionS

    xml.writeStartElement("rentgens");                  // BEGIN rentgens
    for (int i=0; i<mRentgensModel->rowCount(); i++)
    {
        xml.writeEmptyElement("rentgen");
        xml.writeAttribute("name",
                           mRentgensModel->item(i, RentName)->text());
        xml.writeAttribute("src",
                           mRentgensModel->item(i, RentSrc)->text());
        xml.writeAttribute("ppc",
                           mRentgensModel->item(i, RentPpc)->text());
        xml.writeAttribute("com",
                           mRentgensModel->item(i, RentComment)->text());
    }

    xml.writeEndElement();                              // END rentgens

    xml.writeTextElement("comment", mComment);
    xml.writeEndElement();  // pacient
    xml.writeEndDocument();
    file.close();
    //
    return true;            // успішо записано
}

bool Pacient::saveToHtml(const QString &path)
{
    QFile file(path);
    if (!file.open(QIODevice::WriteOnly))
        return false;
    QFileInfo fi(file);
    QString dest ( fi.dir().absolutePath() );
    QDir curDir (App->curTempDir());
    curDir.setNameFilters(ImageFileFilter);

    foreach (QString file, curDir.entryList()) {
        QFile f(App->fileInCurrent(file));
        QDir d(dest);
        qDebug() << "Copying " << file <<  " to " << d.absoluteFilePath(file) << ": " << f.copy( d.absoluteFilePath(file) );
    }
    QFile f(":/logo/logo1.png");
    QDir d(dest);
    f.copy( d.absoluteFilePath(dest) );
    file.resize(0);                 // занулення файлу
    QTextStream out(&file);
    out << genHtml();
    return true;
}

QString Pacient::genHtmlInspections() const
{
    QString res;
    res.append(QString("<h2>Обстеження</h2>"));
    res.append(QString("<table border=\"1\" cellpadding=\"2\" cellspacing=\"2\" width=\"100%\">"));
    // заголовки
    res.append("<tr>");
    for (int i=0; i<InspectionColCount; ++i)
    {
        res.append(QString("<th>%1</th>")
                   .arg(InspectionColStrings.at(i)));
    }
    res.append("</tr>");

    // стрічки
    for (int i=0; i<inspectionsCount(); ++i)
    {
        res.append("<tr>");
        for (int j=0; j<InspectionColCount; ++j)
        {
            QString curStr;
            QVariant curVar = mInspectionsModel->data(mInspectionsModel->index(i, j), Qt::EditRole);
            switch (j) {
            case IC_Date:
                curStr = curVar.toDate().toString(DateFormat);
                break;
            case IC_Limb:
                curStr = LimbStrings.at( curVar.toInt() );
                break;
            case IC_Promeni:
                curStr = curVar.toString();
                break;
            case IC_Prognoz:
                curStr = curVar.toInt()==1 ? checked : unChecked;
                break;
            default:
                if (!curVar.toString().isEmpty())
                    curStr = doubleToQString( curVar.toDouble() );
                else
                    curStr = mDash;
                break;
            }
            res.append(QString("<td>%1</td>").arg(curStr));
        }
        res.append("</tr>");
    }
    res.append(QString("</table>"));
    return res;
}

QString Pacient::genHtmlOperations() const
{
    QString res;
    res.append(QString("<table border=\"1\" cellpadding=\"2\" cellspacing=\"2\" width=\"100%\">"));
    // заголовки
    res.append("<tr>");
    for (int i=0; i<OperationColCount; ++i)
    {
        res.append(QString("<th>%1</th>")
                   .arg(OperationColStrings.at(i)));
    }
    res.append("</tr>");

    // стрічки
    for (int i=0; i<operationsCount(); ++i)
    {
        res.append("<tr>");
        for (int j=0; j<OperationColCount; ++j)
        {
            QString curStr;
            QVariant curVar = mOperationsModel->data(mOperationsModel->index(i, j));
            switch (j) {
            case OC_Date:
                curStr = curVar.toDate().toString(DateFormat);
                break;
            case OC_Age:
                curStr = doubleToQString( curVar.toDouble() );
                break;
            case OC_Limb:
                curStr = LimbStrings.at( curVar.toInt() );
                break;
            case OC_Bone:
                curStr = OperationBones.at( curVar.toInt() );
                break;
            case OC_Location:
                curStr = OperationLocations.at( curVar.toInt() );
                break;
            case OC_Method:
                qDebug() << "Fail here: " << j << OC_Method << curVar;
                curStr = App->mOperationTypes.at( curVar.toInt() );
                break;
            default: // for comment
                curStr = curVar.toString();
                break;
            }
            qDebug() << j << curVar << curStr;
            res.append(QString("<td>%1</td>").arg(curStr));
        }
        res.append("</tr>");
    }
    res.append(QString("</table>"));
    return res;
}

QString Pacient::genHtmlRentgens() const
{
    QString res;
    res.append(QString("<h2>Рентгенограми</h2>"));
    res.append(QString("<table border=\"1\" cellpadding=\"2\" cellspacing=\"2\" width=\"100%\">"));
    res.append("<tr>");
    for (int i=0; i<RENT_COL_COUNT; ++i)
    {
        res.append(QString("<th>%1</th>")
                   .arg(RentgenColStrings.at(i)));
    }
    res.append("</tr>");

    // стрічки
    for (int i=0; i<rentgensCount(); ++i)
    {
        res.append("<tr>");
        for (int j=0; j<RENT_COL_COUNT; ++j)
        {
            QString curStr, imgLink;
            QVariant curVar = mRentgensModel->data(mRentgensModel->index(i, j));

            switch (j) {
            case RentName:
                curStr = curVar.toString();
                break;
            case RentSrc:
                imgLink = QFileInfo(curVar.toString()).fileName();
                curStr =  QString("<a href=\"%1\"><img src=\"%1\" height=\"128\"></a>")
                        .arg(imgLink);
                break;
            case RentPpc:
                curStr = QString::number(curVar.toInt());
                break;
            case RentComment:
                curStr = curVar.toString();
                break;
            default:
                curStr = curVar.toString();
                break;
            }
            res.append(QString("<td>%1</td>").arg(curStr));
        }
        res.append("</tr>");
    }
    res.append(QString("</table>"));
    return res;
}

QString Pacient::genHtml() const
{
    QFile htmlTemplateFile(":/htmls/pacient_template.htm");
    htmlTemplateFile.open(QIODevice::ReadOnly);
    QTextStream in(&htmlTemplateFile);
    in.setCodec(QTextCodec::codecForName("UTF-8"));

    QString blood = QString("%1 %2")
            .arg( BloodTypes.at(mBlood.first) )
            .arg( BloodRh.at(mBlood.second) );
    QString region = RegionStrings.at(mRegion);
    if (!mTown.isEmpty())
        region.append(UkrString(", м. %1").arg(mTown));

    qDebug() << App->pacient()->foto();

    QString htmlContent = in.readAll();
    //    qDebug() << "htmlContent" << htmlContent;
    htmlContent = htmlContent
            .arg( fullName() ) // %1
            .arg( App->pacient()->foto() )
            .arg( mDob.toString(DateFormat) ) // %3
            .arg( blood ) // %4
            .arg( region ) // %5
            .arg( "DZ" )  // %6            // TODO: DIAGNOZ
            .arg( App->pacient()->rentgensCount()>0
                  ? genHtmlRentgens()
                  : "")    //7
            .arg( App->pacient()->inspectionsCount()>0
                  ? genHtmlInspections()
                  : "") // 8
            .arg( App->pacient()->operationsCount()>0
                  ? genHtmlOperations()
                  : "") // 9
            .arg( "strPrognoz" ); // 10      // TODO: PROGNOZ
    return htmlContent;
}

QString Pacient::fullName() const
{
    return QString("%1 %2 %3")
            .arg(mLName)
            .arg(mFName)
            .arg(mMName);
}

void Pacient::createAndSetupModels()
{
    if (mRentgensModel!=NULL)
        delete mRentgensModel;
    mRentgensModel = new QStandardItemModel(this);

    if (mOperationsModel!=NULL)
        delete mOperationsModel;
    mOperationsModel  = new QStandardItemModel(this);
    mOperationsModel->setColumnCount(OperationColCount);
    mOperationsModel->setRowCount(0);
    for (int i=0; i<OperationColCount; ++i)
    {
        mOperationsModel->setHeaderData( i, Qt::Horizontal,
                                         OperationColStrings.at(i), Qt::DisplayRole);
    }

    if (mInspectionsModel!=NULL)
        delete mInspectionsModel;
    mInspectionsModel = new QStandardItemModel(this);
    mInspectionsModel->setColumnCount(InspectionColCount);
    mInspectionsModel->setRowCount(0);
    for (int i=0; i<InspectionColCount; ++i)
    {
        mInspectionsModel->setHeaderData( i, Qt::Horizontal,
                                          InspectionColStrings.at(i), Qt::DisplayRole);
    }
}
